package com.example.dicomviewer.reconstruction.shapes;

import android.opengl.GLES30;
import android.opengl.Matrix;
import android.util.Log;

import com.example.dicomviewer.core.DataArray;
import com.example.dicomviewer.reconstruction.script.GLHelper;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

public class Lung {
    private static final String TAG = "Lung";
    private FloatBuffer vertexBuffer;
    private FloatBuffer colorBuffer;
    private ByteBuffer indicateBuffer;
    private FloatBuffer texureCoordFloatBuffer;
    private FloatBuffer eyePositionIntBuffer;
    private final float[] projectionMatrix = new float[16];
    // 必须要加"precision mediump sampler3D;\n"  否则编译错误
    private final String vertexShaderCode =
            "#version 300 es  \n" +
                    "layout(location = 0) in vec4 vPosition;\n" +
                    "layout(location = 1) in vec4 aColor;\n" +
//                    "layout(location = 2) in vec3 aTexCoord;\n" +
                    "uniform mat4 aMatrix;\n" +
                    "out vec4 vColor; \n" +
                    "out vec3 vTexCoord; \n" +
                    "void main() {\n" +
                    "gl_Position = aMatrix*vPosition;\n" +
                    "vColor=aColor;\n" +
                    "vTexCoord = vPosition.xyz; \n" +
                    "}";

    private final String fragmentShaderCode =
            "#version 300 es  \n" +
                    "precision mediump float;\n" +
                    "precision highp sampler3D;\n" +
                    "out vec4 fragColor;\n" +
                    "in vec4 vColor; \n" +
                    "in vec3 vTexCoord; \n" +
                    "uniform sampler3D sa;\n" +
                    "uniform vec3 eyes; \n" +
                    "vec3 dir,delta_dir;\n" +
                    "float _TMP3,_TMP4,alpha_sample,alpha_acc=0.0f,step = 0.01f;\n" +
                    "void main() {\n" +
                    "vec3 vec = vTexCoord,new_coord;\n" +
                    "dir = -eyes; \n" +
                    "dir = normalize(dir); \n" +
                    "delta_dir = dir*step; \n" +
                    "int i = 0;\n" +
                    "vec4 color_sample,col_acc=vec4(0.0f,0.0f,0.0f,0.00f);\n" +
                    "for(i=0;i<200;i=i+1){ \n" +
//                    "   col_acc  = vec4(abs(eyes.x)/1.0f,abs(eyes.y/1.0f),abs(eyes.z/1.0f),1.0f);\n"+
//                    "   break;\n"+
//                    "   new_coord = vec3((vec.x+1.1f),(vec.y+0.5f),(vec.z+0.5f)/2.0f);\n" +
                    "   new_coord = vec3((vec.x+1.0f)*0.6,(vec.y+0.8f)*0.6,(vec.z)*4.0f+0.525f);\n" +
//                    "   col_acc  = vec4(new_coord,1.0f);\n"+
//                    "   break;\n"+
                    "   color_sample  = texture(sa,new_coord); \n" +
//                    "   if( color_sample.a < 0.1f) {continue;}\n" +
//                    "   if( color_sample.a == 1.0f) {continue;}\n" +
//                    "   if(  (color_sample.r!=1.0f && color_sample.g!=1.0f && color_sample.b!=1.0f)) {  col_acc = color_sample ; break;}\n" +
                    "   alpha_sample  = color_sample.a; \n" +
                    "   col_acc      += (1.0f - alpha_acc) * color_sample * alpha_sample ; \n" +
                    "   alpha_acc    += alpha_sample*(1.0f-alpha_acc); \n" +
                    "   vec          += delta_dir;\n" +
                    "   if(alpha_acc >= 0.95f) break; \n" +
                    "}\n" +
                    "if( col_acc.a <0.1f){ discard;}\n" +
                    "fragColor = col_acc;\n" +
                    "}";

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;
    static final int COORDS_PER_Tex = 2;

    static float[] triangleCoords = null;
    static float[] texureCoords = null;
    private final int mProgram;
    // Set color with red, green, blue and alpha (opacity) values
    float[] color = {
            0.0f, 1.0f, 0.0f,
            0.0f, 1.0f, 0.0f,
            1.0f, 0.5f, 0.0f,
            1.0f, 0.5f, 0.0f,
            1.0f, 0.0f, 0.0f,
            1.0f, 0.0f, 0.0f,
            0.0f, 0.0f, 1.0f,
            1.0f, 0.0f, 1.0f
    };
    byte indicates[] = null;

    public static int loadShader(int type, String shaderCode) {

        // create a vertex shader type (GLES30.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES30.GL_FRAGMENT_SHADER)
        // todo: 如果先绑定纹理，再新建lung，那么会卡在这一行语句，没有glTexImage3D的话就没问题
        Log.d(TAG, "create Shader");
        int shader = GLES30.glCreateShader(type);

        Log.d(TAG, "glShaderSource");
        // add the source code to the shader and compile it
        GLES30.glShaderSource(shader, shaderCode);

        Log.d(TAG, "glCompileSource");
        GLES30.glCompileShader(shader);

        return shader;
    }

    private void initData() {
        DataArray dataArray = DataArray.getInstance();
        int width = dataArray.getWidth();
        int hight = dataArray.getHight();
        int slice = dataArray.getSliceSize();
        float scale = 1.0f * Math.max(Math.max(width, hight), slice);
        // 应除以略大于 sqrt(2) 的数
        float xBorder = width / scale / 1.42f;
        float yBorder = hight / scale / 1.42f;
        float zBorder = slice / scale / 1.42f;
        triangleCoords = new float[]{
                // 右上前
                xBorder, yBorder, zBorder,
                // 右下前
                xBorder, yBorder, -zBorder,
                // 右上后
                -xBorder, yBorder, zBorder,
                // 右下后
                -xBorder, yBorder, -zBorder,

                // 左上前
                xBorder, -yBorder, zBorder,
                // 左下前
                xBorder, -yBorder, -zBorder,
                // 左上后
                -xBorder, -yBorder, zBorder,
                // 左下后
                -xBorder, -yBorder, -zBorder,
        };

        indicates = new byte[]{
                //上
                0, 1, 2,
                1, 2, 3,
                //下
                4, 5, 6,
                5, 6, 7,
                //左
                2, 3, 6,
                3, 6, 7,
                //右
                0, 1, 4,
                1, 4, 5,
                //前
                0, 2, 4,
                2, 4, 6,
                //后
                1, 3, 7,
                1, 7, 5
        };
        texureCoords = new float[]{
                // 右上前
                2 * xBorder, 2 * yBorder, 2 * zBorder,
                // 右下前
                2 * xBorder, 2 * yBorder, 0,
                // 右上后
                0, 2 * yBorder, 2 * zBorder,
                // 右下后
                0, 2 * yBorder, 0,

                // 左上前
                2 * xBorder, 0, 2 * zBorder,
                // 左下前
                2 * xBorder, 0, 0,
                // 左上后
                0, 0, 2 * zBorder,
                // 左下后
                0, 0, 0,
        };
    }

    // 定义一个工具方法，将float[]buffer数据转换为OpenGL ES所需要的FloatBuffer
    private FloatBuffer floatBufferUtil(float[] arr) {
        FloatBuffer mbuffer;
        // 初始化ByteBuffer，长度为arr.length * 4,因为float占4个字节
        ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
        // 数组排列用nativeOrder
        qbb.order(ByteOrder.nativeOrder());

        mbuffer = qbb.asFloatBuffer();
        mbuffer.put(arr);
        mbuffer.position(0);

        return mbuffer;
    }

    public Lung() {
        Log.d(TAG, "begin Square");
        Log.d(TAG, "init data");
        initData();
        indicateBuffer = ByteBuffer.allocate(indicates.length);
        indicateBuffer.put(indicates);
        indicateBuffer.position(0);

        Log.d(TAG, "begin bb allocate");
        // initialize vertex byte buffer for shape coordinates
        // (number of coordinate values * 4 bytes per float)
//        ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4);
//        // use the device hardware's native byte order
//        bb.order(ByteOrder.nativeOrder());
//        // create a floating point buffer from the ByteBuffer
//        vertexBuffer = bb.asFloatBuffer();
//        // add the coordinates to the FloatBuffer
//        vertexBuffer.put(triangleCoords);
//        // set the buffer to read the first coordinate
//        vertexBuffer.position(0);
        this.vertexBuffer = floatBufferUtil(triangleCoords);


        Log.d(TAG, "end bb allocate");

        Log.d(TAG, "begin create texureCoord");
        this.texureCoordFloatBuffer = floatBufferUtil(texureCoords);
        Log.d(TAG, "end create texureCoord");


        this.colorBuffer = floatBufferUtil(color);


        Log.d(TAG, "begin create empty OpenGL ES Program");
        int vertexsharder = GLHelper.compileScript(GLES30.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentsharder = GLHelper.compileScript(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode);
        mProgram = GLHelper.linkAttach(vertexsharder, fragmentsharder);
        boolean isOK = GLHelper.checkProgram(mProgram);
        if (isOK) {
            Log.d(TAG, "create Program success");
        } else {
            Log.e(TAG, "create Program failed");
        }

        Log.d(TAG, "end Square");
    }

    private int mPositionHandle;
    private int mColorHandle;
    private int mTexCoordHandle;


    public void onDraw(int textureId, float[] eyes, float[] up, float scale) {
        Log.d(TAG, "begin onDrawFrame");
        // Add program to OpenGL ES environment

        /*
         * 开始创建矩阵
         * */
        Log.d(TAG, "creat viewMatrix");
        // 创建视图矩阵
        float[] viewMatrix = new float[16];
        Matrix.setIdentityM(viewMatrix, 0);
        Matrix.setLookAtM(viewMatrix, 0, eyes[0], eyes[1], eyes[2], 0f, 0f, 0f, up[0], up[1], up[2]);


        Log.d(TAG, "creat mMVPMatrix");
        // 创建
        // 计算绘制物体最终在屏幕上的投影和视图变换
        float[] mMVPMatrix = new float[16];
        Matrix.multiplyMM(mMVPMatrix, 0, viewMatrix, 0, orthMatrix, 0);


        Log.d(TAG, "creat modelMatrix");
        // 模型矩阵
        float[] modelMatrix = new float[16];
        Matrix.setIdentityM(modelMatrix, 0);
        Matrix.rotateM(modelMatrix, 0, rotateAgree, 0.5f, 0.5f, 0.5f);

        // 缩放
        Matrix.scaleM(modelMatrix, 0, scale, scale, scale);

        Log.d(TAG, "creat mvpMatrix");
        float[] mvpMatrix = new float[16];
        Matrix.multiplyMM(projectionMatrix, 0, viewMatrix, 0, modelMatrix, 0);
        Matrix.multiplyMM(mvpMatrix, 0, orthMatrix, 0, projectionMatrix, 0);
        Log.d(TAG, "glGetUniformLocation");
        int aMatrixPos = GLES30.glGetUniformLocation(mProgram, "aMatrix");
        Log.d(TAG, "glUniformMatrix4fv");
        GLES30.glUniformMatrix4fv(aMatrixPos, 1, false, mvpMatrix, 0);
        /*
         * 结束创建矩阵
         * */

        Log.d(TAG, "glUseProgram");
        GLES30.glUseProgram(mProgram);
        // todo 如果是先创建lung，再创建纹理，那么就会卡在这里，感觉应该是这一个

        Log.d(TAG, "glUniform1i <Attention!> textureId");
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glActiveTexture(GLES30.GL_TEXTURE1);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_3D, textureId);
        GLES30.glUniform1i(GLES30.glGetUniformLocation(mProgram, "sa"), textureId);
        GLES30.glUniform3f(GLES30.glGetUniformLocation(mProgram, "eyes"), eyes[0], eyes[1], eyes[2]);


        // 开始设置顶点数组
        // get handle to vertex shader's vPosition member
        Log.d(TAG, "glGetAttribLocation");
        mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");
        // Enable a handle to the triangle vertices
        Log.d(TAG, "glEnableVertexAtribArray");
        GLES30.glEnableVertexAttribArray(mPositionHandle);
        // Prepare the triangle coordinate data
        Log.d(TAG, "glVertexAttribPointer");
        GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES30.GL_FLOAT, false, 0, vertexBuffer);


        // 开始设置颜色数组
        Log.d(TAG, "glGetAttribLocation");
        // get handle to fragment shader's vColor member
        mColorHandle = GLES30.glGetAttribLocation(mProgram, "aColor");

        // Set color for drawing the triangle
        Log.d(TAG, "glEnableVertexAttribArry");
        GLES30.glEnableVertexAttribArray(mColorHandle);
        Log.d(TAG, "glVertexAttribPointer");
        GLES30.glVertexAttribPointer(mColorHandle, 3,
                GLES30.GL_FLOAT, false,
                0, colorBuffer);

        // Draw the triangle
        Log.d(TAG, "glDrawElements");
        GLES30.glDrawElements(GLES30.GL_TRIANGLES, indicates.length,
                GLES30.GL_UNSIGNED_BYTE, indicateBuffer);


        Log.d(TAG, "glDisableVertexAttribArray");
        // Disable vertex array
        GLES30.glDisableVertexAttribArray(mPositionHandle);
    }

    int rotateAgree = 0;
    float[] orthMatrix = new float[16];

    public void onSurfaceChange(int width, int height) {
        GLES30.glViewport(0, 0, width, height);
        final float aspectRatio = width > height ?
                ((float) width / (float) height)
                : ((float) height / (float) width);
        //正交投影
        Matrix.setIdentityM(orthMatrix, 0);
        if (width > height) {
            // 将远平面修改为100f，能够减少显示不全的问题
            Matrix.orthoM(orthMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 100f);
        } else {
            Matrix.orthoM(orthMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 100f);
        }
    }

    private void createVertextBuffer() {
        int[] value = new int[1];
        GLES30.glGenBuffers(1, value, 0);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, value[0]);
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, vertexBuffer.capacity() * 4, vertexBuffer, GLES30.GL_STATIC_DRAW);
    }

}
